Shiny App Experiment Lab
Libraries
library(here)
library(dplyr)
library(arrow)
library(tidyr)
library(geojsonio)
Reading data
census_dataset <- open_dataset(here("data", "processed", "parquet_data_coords"))
census_dataset
FileSystemDataset with 33 Parquet files
NOM_MUN: string
NOM_LOC: string
LONGITUD: string
LATITUD: string
POBTOT: double
REL_H_M: string
POB0_14: string
POB15_64: string
POB65_MAS: string
P_0A4: string
P_0A4_F: string
P_0A4_M: string
P_5A9: string
P_5A9_F: string
P_5A9_M: string
P_10A14: string
P_10A14_F: string
P_10A14_M: string
P_15A19: string
P_15A19_F: string
P_15A19_M: string
P_20A24: string
P_20A24_F: string
P_20A24_M: string
P_25A29: string
P_25A29_F: string
P_25A29_M: string
P_30A34: string
P_30A34_F: string
P_30A34_M: string
P_35A39: string
P_35A39_F: string
P_35A39_M: string
P_40A44: string
P_40A44_F: string
P_40A44_M: string
P_45A49: string
P_45A49_F: string
P_45A49_M: string
P_50A54: string
P_50A54_F: string
P_50A54_M: string
P_55A59: string
P_55A59_F: string
P_55A59_M: string
P_60A64: string
P_60A64_F: string
P_60A64_M: string
P_65A69: string
P_65A69_F: string
P_65A69_M: string
P_70A74: string
P_70A74_F: string
P_70A74_M: string
P_75A79: string
P_75A79_F: string
P_75A79_M: string
P_80A84: string
P_80A84_F: string
P_80A84_M: string
P_85YMAS: string
P_85YMAS_F: string
P_85YMAS_M: string
PROM_HNV: string
PNACENT: string
PNACENT_F: string
PNACENT_M: string
PNACOE: string
PNACOE_F: string
PNACOE_M: string
longitude_decimal: double
latitude_decimal: double
NOM_ENT: string
See $metadata for additional Schema metadata
Reading specific data
pueb_norm <- census_dataset |>
filter(NOM_ENT=="Puebla") |>
collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
pueb_norm
unique(pueb_norm$NOM_MUN)
[1] "Total de la entidad Puebla" "Acajete" "Acateno" "Acatlán"
[5] "Acatzingo" "Acteopan" "Ahuacatlán" "Ahuatlán"
[9] "Ahuazotepec" "Ahuehuetitla" "Ajalpan" "Albino Zertuche"
[13] "Aljojuca" "Altepexi" "Amixtlán" "Amozoc"
[17] "Aquixtla" "Atempan" "Atexcal" "Atlixco"
[21] "Atoyatempan" "Atzala" "Atzitzihuacán" "Atzitzintla"
[25] "Axutla" "Ayotoxco de Guerrero" "Calpan" "Caltepec"
[29] "Camocuautla" "Caxhuacan" "Coatepec" "Coatzingo"
[33] "Cohetzala" "Cohuecan" "Coronango" "Coxcatlán"
[37] "Coyomeapan" "Coyotepec" "Cuapiaxtla de Madero" "Cuautempan"
[41] "Cuautinchán" "Cuautlancingo" "Cuayuca de Andrade" "Cuetzalan del Progreso"
[45] "Cuyoaco" "Chalchicomula de Sesma" "Chapulco" "Chiautla"
[49] "Chiautzingo" "Chiconcuautla" "Chichiquila" "Chietla"
[53] "Chigmecatitlán" "Chignahuapan" "Chignautla" "Chila"
[57] "Chila de la Sal" "Honey" "Chilchotla" "Chinantla"
[61] "Domingo Arenas" "Eloxochitlán" "Epatlán" "Esperanza"
[65] "Francisco Z. Mena" "General Felipe Ángeles" "Guadalupe" "Guadalupe Victoria"
[69] "Hermenegildo Galeana" "Huaquechula" "Huatlatlauca" "Huauchinango"
[73] "Huehuetla" "Huehuetlán el Chico" "Huejotzingo" "Hueyapan"
[77] "Hueytamalco" "Hueytlalpan" "Huitzilan de Serdán" "Huitziltepec"
[81] "Atlequizayan" "Ixcamilpa de Guerrero" "Ixcaquixtla" "Ixtacamaxtitlán"
[85] "Ixtepec" "Izúcar de Matamoros" "Jalpan" "Jolalpan"
[89] "Jonotla" "Jopala" "Juan C. Bonilla" "Juan Galindo"
[93] "Juan N. Méndez" "Lafragua" "Libres" "La Magdalena Tlatlauquitepec"
[97] "Mazapiltepec de Juárez" "Mixtla" "Molcaxac" "Cañada Morelos"
[101] "Naupan" "Nauzontla" "Nealtican" "Nicolás Bravo"
[105] "Nopalucan" "Ocotepec" "Ocoyucan" "Olintla"
[109] "Oriental" "Pahuatlán" "Palmar de Bravo" "Pantepec"
[113] "Petlalcingo" "Piaxtla" "Puebla" "Quecholac"
[117] "Quimixtlán" "Rafael Lara Grajales" "Los Reyes de Juárez" "San Andrés Cholula"
[121] "San Antonio Cañada" "San Diego la Mesa Tochimiltzingo" "San Felipe Teotlalcingo" "San Felipe Tepatlán"
[125] "San Gabriel Chilac" "San Gregorio Atzompa" "San Jerónimo Tecuanipan" "San Jerónimo Xayacatlán"
[129] "San José Chiapa" "San José Miahuatlán" "San Juan Atenco" "San Juan Atzompa"
[133] "San Martín Texmelucan" "San Martín Totoltepec" "San Matías Tlalancaleca" "San Miguel Ixitlán"
[137] "San Miguel Xoxtla" "San Nicolás Buenos Aires" "San Nicolás de los Ranchos" "San Pablo Anicano"
[141] "San Pedro Cholula" "San Pedro Yeloixtlahuaca" "San Salvador el Seco" "San Salvador el Verde"
[145] "San Salvador Huixcolotla" "San Sebastián Tlacotepec" "Santa Catarina Tlaltempan" "Santa Inés Ahuatempan"
[149] "Santa Isabel Cholula" "Santiago Miahuatlán" "Huehuetlán el Grande" "Santo Tomás Hueyotlipan"
[153] "Soltepec" "Tecali de Herrera" "Tecamachalco" "Tecomatlán"
[157] "Tehuacán" "Tehuitzingo" "Tenampulco" "Teopantlán"
[161] "Teotlalco" "Tepanco de López" "Tepango de Rodríguez" "Tepatlaxco de Hidalgo"
[165] "Tepeaca" "Tepemaxalco" "Tepeojuma" "Tepetzintla"
[169] "Tepexco" "Tepexi de Rodríguez" "Tepeyahualco" "Tepeyahualco de Cuauhtémoc"
[173] "Tetela de Ocampo" "Teteles de Avila Castillo" "Teziutlán" "Tianguismanalco"
[177] "Tilapa" "Tlacotepec de Benito Juárez" "Tlacuilotepec" "Tlachichuca"
[181] "Tlahuapan" "Tlaltenango" "Tlanepantla" "Tlaola"
[185] "Tlapacoya" "Tlapanalá" "Tlatlauquitepec" "Tlaxco"
[189] "Tochimilco" "Tochtepec" "Totoltepec de Guerrero" "Tulcingo"
[193] "Tuzamapan de Galeana" "Tzicatlacoyan" "Venustiano Carranza" "Vicente Guerrero"
[197] "Xayacatlán de Bravo" "Xicotepec" "Xicotlán" "Xiutetelco"
[201] "Xochiapulco" "Xochiltepec" "Xochitlán de Vicente Suárez" "Xochitlán Todos Santos"
[205] "Yaonáhuac" "Yehualtepec" "Zacapala" "Zacapoaxtla"
[209] "Zacatlán" "Zapotitlán" "Zapotitlán de Méndez" "Zaragoza"
[213] "Zautla" "Zihuateutla" "Zinacatepec" "Zongozotla"
[217] "Zoquiapan" "Zoquitlán"
Example
extract_coordinates <- function(data, municipality, locality) {
selected_location <- data |>
filter(NOM_MUN == municipality, NOM_LOC == locality)
coordinates <- tibble(
long = selected_location$longitude_decimal,
lat = selected_location$latitude_decimal
)
return(coordinates)
}
municipality <- "Acajete"
locality <- "Santa Isabel Tepetzala"
red_point <- extract_coordinates(pueb_norm, municipality, locality) |>
slice(1)
# red_point <- data.frame(long = -98.2035, lat = 19.0414)
geojson_file <- geojson_read("../data/processed/mexico.geojson", what = "sp")
filtered_geojson <- geojson_file |>
filter(name == "Puebla")
ggplot() +
geom_polygon(data = filtered_geojson,
aes(x = long, y = lat, group = group),
fill = "lightgray", color = "white") +
geom_point(data = red_point, aes(x = long, y = lat), color = "red", size = 3) +
theme_void() +
coord_map()
Warning: `fortify(<SpatialPolygonsDataFrame>)` was deprecated in ggplot2 3.4.4.
Please migrate to sf.Regions defined for each Polygons

Population graph
pueb_norm
Getting column names
column_names <- names(pueb_norm)
column_names
[1] "NOM_MUN" "NOM_LOC" "LONGITUD" "LATITUD" "POBTOT" "REL_H_M" "POB0_14"
[8] "POB15_64" "POB65_MAS" "P_0A4" "P_0A4_F" "P_0A4_M" "P_5A9" "P_5A9_F"
[15] "P_5A9_M" "P_10A14" "P_10A14_F" "P_10A14_M" "P_15A19" "P_15A19_F" "P_15A19_M"
[22] "P_20A24" "P_20A24_F" "P_20A24_M" "P_25A29" "P_25A29_F" "P_25A29_M" "P_30A34"
[29] "P_30A34_F" "P_30A34_M" "P_35A39" "P_35A39_F" "P_35A39_M" "P_40A44" "P_40A44_F"
[36] "P_40A44_M" "P_45A49" "P_45A49_F" "P_45A49_M" "P_50A54" "P_50A54_F" "P_50A54_M"
[43] "P_55A59" "P_55A59_F" "P_55A59_M" "P_60A64" "P_60A64_F" "P_60A64_M" "P_65A69"
[50] "P_65A69_F" "P_65A69_M" "P_70A74" "P_70A74_F" "P_70A74_M" "P_75A79" "P_75A79_F"
[57] "P_75A79_M" "P_80A84" "P_80A84_F" "P_80A84_M" "P_85YMAS" "P_85YMAS_F" "P_85YMAS_M"
[64] "PROM_HNV" "PNACENT" "PNACENT_F" "PNACENT_M" "PNACOE" "PNACOE_F" "PNACOE_M"
[71] "longitude_decimal" "latitude_decimal" "NOM_ENT"
Getting column names that have to do with population age cohorts
(Masculine/Feminine)
matching_columns <- grep("^P_.*[MF]$", column_names, value = TRUE)
matching_columns
[1] "P_0A4_F" "P_0A4_M" "P_5A9_F" "P_5A9_M" "P_10A14_F" "P_10A14_M" "P_15A19_F" "P_15A19_M" "P_20A24_F" "P_20A24_M" "P_25A29_F"
[12] "P_25A29_M" "P_30A34_F" "P_30A34_M" "P_35A39_F" "P_35A39_M" "P_40A44_F" "P_40A44_M" "P_45A49_F" "P_45A49_M" "P_50A54_F" "P_50A54_M"
[23] "P_55A59_F" "P_55A59_M" "P_60A64_F" "P_60A64_M" "P_65A69_F" "P_65A69_M" "P_70A74_F" "P_70A74_M" "P_75A79_F" "P_75A79_M" "P_80A84_F"
[34] "P_80A84_M" "P_85YMAS_F" "P_85YMAS_M"
Separate by sex
ending_in_M <- character(0)
ending_in_F <- character(0)
for (col_name in matching_columns) {
if (endsWith(col_name, "M")) {
ending_in_M <- c(ending_in_M, col_name)
} else if (endsWith(col_name, "F")) {
ending_in_F <- c(ending_in_F, col_name)
}
}
print("Column names ending in M:")
[1] "Column names ending in M:"
print(ending_in_M)
[1] "P_0A4_M" "P_5A9_M" "P_10A14_M" "P_15A19_M" "P_20A24_M" "P_25A29_M" "P_30A34_M" "P_35A39_M" "P_40A44_M" "P_45A49_M" "P_50A54_M"
[12] "P_55A59_M" "P_60A64_M" "P_65A69_M" "P_70A74_M" "P_75A79_M" "P_80A84_M" "P_85YMAS_M"
print("Column names ending in F:")
[1] "Column names ending in F:"
print(ending_in_F)
[1] "P_0A4_F" "P_5A9_F" "P_10A14_F" "P_15A19_F" "P_20A24_F" "P_25A29_F" "P_30A34_F" "P_35A39_F" "P_40A44_F" "P_45A49_F" "P_50A54_F"
[12] "P_55A59_F" "P_60A64_F" "P_65A69_F" "P_70A74_F" "P_75A79_F" "P_80A84_F" "P_85YMAS_F"
cohort_names_m <- c("P_0A4_M",
"P_5A9_M",
"P_10A14_M",
"P_15A19_M",
"P_20A24_M",
"P_25A29_M",
"P_30A34_M",
"P_35A39_M",
"P_40A44_M",
"P_45A49_M",
"P_50A54_M",
"P_55A59_M",
"P_60A64_M",
"P_65A69_M",
"P_70A74_M",
"P_75A79_M",
"P_80A84_M",
"P_85YMAS_M")
cohort_names_f <- c("P_0A4_F",
"P_5A9_F",
"P_10A14_F",
"P_15A19_F",
"P_20A24_F",
"P_25A29_F",
"P_30A34_F",
"P_35A39_F",
"P_40A44_F",
"P_45A49_F",
"P_50A54_F",
"P_55A59_F",
"P_60A64_F",
"P_65A69_F",
"P_70A74_F",
"P_75A79_F",
"P_80A84_F",
"P_85YMAS_F")
municipality <- "Acajete"
locality <- "San Javier"
pueb_norm_filt <- pueb_norm |>
filter(NOM_MUN == municipality, NOM_LOC == locality)
cohort_counts_m <- as.numeric(pueb_norm_filt[1,cohort_names_m])
cohort_counts_f <- as.numeric(pueb_norm_filt[1,cohort_names_f])
data <- tibble(
Cohort = c(cohort_names_m, cohort_names_f),
Count = c(cohort_counts_m, cohort_counts_f),
Sex = rep(c("Male", "Female"), each = length(cohort_names_m))
)
# Plotting population pyramid
ggplot(data, aes(x = reorder(Cohort, -Count), y = Count, fill = Sex)) +
geom_bar(stat = "identity", position = "identity") +
scale_fill_manual(values = c("blue", "pink")) +
coord_flip() +
labs(title = "Population Pyramid",
x = "Population Count",
y = "Age Cohort",
fill = "Sex") +
theme_minimal()

new_ages <- c("0-4",
"5-9",
"10-14",
"15-19",
"20-24",
"25-29",
"30-34",
"35-39",
"40-44",
"45-49",
"50-54",
"55-59",
"60-64",
"65-69",
"70-74",
"75-79",
"80-84",
"85+")
data <- tibble(
Age = paste0(new_ages),
Male = sample(200:1000, length(cohort_names_m), replace = TRUE),
Female = sample(200:1000, length(cohort_names_f), replace = TRUE)
)
data_long <- pivot_longer(
data,
cols = c(Male, Female),
names_to = "Sex",
values_to = "Population"
)
basic_plot <- ggplot(data_long, aes(x = Age, y = ifelse(Sex == "Male", -Population, Population), fill = Sex)) +
geom_bar(stat = "identity") +
scale_y_continuous(labels = abs, limits = max(data_long$Population) * c(-1, 1)) +
coord_flip() +
theme_minimal() +
labs(x = "Age", y = "Population", fill = "Sex", title = "Population Pyramid")
basic_plot

Complete pipeline
census_dataset <- open_dataset(here("data", "processed", "parquet_data_coords"))
pueb_norm <- census_dataset |>
filter(NOM_ENT=="Puebla") |>
collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
municipality <- "Acajete"
locality <- "San Javier"
pueb_norm_filt <- pueb_norm |>
filter(NOM_MUN == municipality, NOM_LOC == locality)
cohort_names_m <- c("P_0A4_M",
"P_5A9_M",
"P_10A14_M",
"P_15A19_M",
"P_20A24_M",
"P_25A29_M",
"P_30A34_M",
"P_35A39_M",
"P_40A44_M",
"P_45A49_M",
"P_50A54_M",
"P_55A59_M",
"P_60A64_M",
"P_65A69_M",
"P_70A74_M",
"P_75A79_M",
"P_80A84_M",
"P_85YMAS_M")
cohort_names_f <- c("P_0A4_F",
"P_5A9_F",
"P_10A14_F",
"P_15A19_F",
"P_20A24_F",
"P_25A29_F",
"P_30A34_F",
"P_35A39_F",
"P_40A44_F",
"P_45A49_F",
"P_50A54_F",
"P_55A59_F",
"P_60A64_F",
"P_65A69_F",
"P_70A74_F",
"P_75A79_F",
"P_80A84_F",
"P_85YMAS_F")
new_ages <- c("0-4",
"5-9",
"10-14",
"15-19",
"20-24",
"25-29",
"30-34",
"35-39",
"40-44",
"45-49",
"50-54",
"55-59",
"60-64",
"65-69",
"70-74",
"75-79",
"80-84",
"85+")
data <- tibble(
Age = paste0(new_ages),
Male = as.numeric(pueb_norm_filt[1,cohort_names_m]),
Female = as.numeric(pueb_norm_filt[1,cohort_names_f])
)
data_long <- pivot_longer(
data,
cols = c(Male, Female),
names_to = "Sex",
values_to = "Population"
)
basic_plot <- ggplot(data_long, aes(x = Age, y = ifelse(Sex == "Male", -Population, Population), fill = Sex)) +
geom_bar(stat = "identity") +
scale_y_continuous(labels = abs, limits = max(data_long$Population) * c(-1, 1)) +
coord_flip() +
theme_minimal() +
labs(x = "Age", y = "Population", fill = "Sex", title = "Population Pyramid")
basic_plot

Card
card <- census_dataset |>
filter(
NOM_ENT == "Puebla",
NOM_MUN == "Acateno",
NOM_LOC == "Santa Andrea"
) |>
collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
total_population <- as.numeric(card[1, c("POBTOT")], na.rm = TRUE)
paste("Total Population:", total_population)
[1] "Total Population: 1"
Pie
tot <- census_dataset |>
filter(NOM_ENT=="Total nacional") |>
collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
tot
# origin <- census_dataset |>
# filter(
# NOM_ENT == "Puebla",
# NOM_MUN == "Acateno",
# NOM_LOC == "Santa Andrea"
# ) |>
# collect()
tot
# Extract birth data
birth_local <- as.numeric(tot[1, "PNACENT"])
birth_another <- as.numeric(tot[1, "PNACOE"])
# Debugging output
print(paste("Birth Local:", birth_local))
[1] "Birth Local: 102724322"
print(paste("Birth Another:", birth_another))
[1] "Birth Another: 21611963"
# Create ratio dataframe
ratio_df <- tibble(
Category = c("Local", "Other"),
Ratio = c(birth_local, birth_another)
)
# Calculate percentages
ratio_df$Percentage <- ratio_df$Ratio / sum(ratio_df$Ratio) * 100
# Plot the pie chart
gg <- ggplot(ratio_df, aes(x = "", y = Ratio, fill = Category)) +
geom_bar(stat = "identity", width = 1) +
coord_polar(theta = "y") +
theme_void() +
theme(legend.position = "bottom") +
scale_fill_manual(values = c("#00BFC4", "#F8766D")) +
geom_text(aes(label = paste0(round(Percentage), "%")),
position = position_stack(vjust = 0.5),
size = 5, color = "white", fontface = "bold")
gg

NA
Read CSV from URL
test_csv <- read.csv("https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA")
test_csv
NA
filtered_data <- filter(test_csv, NOM_ENT == "Puebla")
filtered_data
Download with curl
library(curl)
# Define the URL of the CSV file
url <- "https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA"
response <- curl::curl_fetch_memory(url)
if (response$status_code == 200) {
csv_content <- rawToChar(response$content)
test_csv <- read.csv(text = csv_content)
print(test_csv)
}
library(RCurl)
x <- getURL("https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA")
y <- read.csv(text = x)
y
test_2 <- arrow::read_parquet("https://github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/tree/master/data/processed/parquet_data_coords.parquet")
Error: Invalid: Parquet magic bytes not found in footer. Either the file is corrupted or this is not a parquet file.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogTWFyY28gUG9sbyBCcmF2byBNb250aWVsDQpkYXRlOiAyMDIwLTA0LTIzDQotLS0NCg0KIyBTaGlueSBBcHAgRXhwZXJpbWVudCBMYWINCg0KIyMjIExpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGFycm93KQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2VvanNvbmlvKQ0KYGBgDQoNCiMjIyBSZWFkaW5nIGRhdGENCg0KYGBge3J9DQpjZW5zdXNfZGF0YXNldCA8LSBvcGVuX2RhdGFzZXQoaGVyZSgiZGF0YSIsICJwcm9jZXNzZWQiLCAicGFycXVldF9kYXRhX2Nvb3JkcyIpKQ0KY2Vuc3VzX2RhdGFzZXQNCg0KYGBgDQoNCiMjIyBSZWFkaW5nIHNwZWNpZmljIGRhdGENCg0KYGBge3J9DQpwdWViX25vcm0gPC0gY2Vuc3VzX2RhdGFzZXQgfD4NCiAgICBmaWx0ZXIoTk9NX0VOVD09IlB1ZWJsYSIpIHw+IA0KICAgIGNvbGxlY3QoKQ0KDQpwdWViX25vcm0NCmBgYA0KDQpgYGB7cn0NCnVuaXF1ZShwdWViX25vcm0kTk9NX01VTikNCmBgYA0KDQojIyMgRXhhbXBsZQ0KDQpgYGB7cn0NCmV4dHJhY3RfY29vcmRpbmF0ZXMgPC0gZnVuY3Rpb24oZGF0YSwgbXVuaWNpcGFsaXR5LCBsb2NhbGl0eSkgew0KICBzZWxlY3RlZF9sb2NhdGlvbiA8LSBkYXRhIHw+IA0KICAgIGZpbHRlcihOT01fTVVOID09IG11bmljaXBhbGl0eSwgTk9NX0xPQyA9PSBsb2NhbGl0eSkNCiAgDQogIGNvb3JkaW5hdGVzIDwtIHRpYmJsZSgNCiAgICBsb25nID0gc2VsZWN0ZWRfbG9jYXRpb24kbG9uZ2l0dWRlX2RlY2ltYWwsDQogICAgbGF0ID0gc2VsZWN0ZWRfbG9jYXRpb24kbGF0aXR1ZGVfZGVjaW1hbA0KICApDQogIA0KICByZXR1cm4oY29vcmRpbmF0ZXMpDQp9DQoNCg0KbXVuaWNpcGFsaXR5IDwtICJBY2FqZXRlIg0KbG9jYWxpdHkgPC0gIlNhbnRhIElzYWJlbCBUZXBldHphbGEiDQoNCnJlZF9wb2ludCA8LSBleHRyYWN0X2Nvb3JkaW5hdGVzKHB1ZWJfbm9ybSwgbXVuaWNpcGFsaXR5LCBsb2NhbGl0eSkgfD4gDQogIHNsaWNlKDEpDQoNCiMgcmVkX3BvaW50IDwtIGRhdGEuZnJhbWUobG9uZyA9IC05OC4yMDM1LCBsYXQgPSAxOS4wNDE0KQ0KZ2VvanNvbl9maWxlIDwtIGdlb2pzb25fcmVhZCgiLi4vZGF0YS9wcm9jZXNzZWQvbWV4aWNvLmdlb2pzb24iLCAgd2hhdCA9ICJzcCIpDQoNCmZpbHRlcmVkX2dlb2pzb24gPC0gZ2VvanNvbl9maWxlIHw+IA0KICBmaWx0ZXIobmFtZSA9PSAiUHVlYmxhIikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvbHlnb24oZGF0YSA9IGZpbHRlcmVkX2dlb2pzb24sDQogICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLA0KICAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGdyYXkiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcmVkX3BvaW50LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKw0KICB0aGVtZV92b2lkKCkgKw0KICBjb29yZF9tYXAoKQ0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIGdyYXBoDQoNCmBgYHtyfQ0KcHVlYl9ub3JtDQpgYGANCg0KIyMjIEdldHRpbmcgY29sdW1uIG5hbWVzDQoNCmBgYHtyfQ0KY29sdW1uX25hbWVzIDwtIG5hbWVzKHB1ZWJfbm9ybSkNCmNvbHVtbl9uYW1lcw0KYGBgDQoNCiMjIyBHZXR0aW5nIGNvbHVtbiBuYW1lcyB0aGF0IGhhdmUgdG8gZG8gd2l0aCBwb3B1bGF0aW9uIGFnZSBjb2hvcnRzIChNYXNjdWxpbmUvRmVtaW5pbmUpDQoNCmBgYHtyfQ0KbWF0Y2hpbmdfY29sdW1ucyA8LSBncmVwKCJeUF8uKltNRl0kIiwgY29sdW1uX25hbWVzLCB2YWx1ZSA9IFRSVUUpDQptYXRjaGluZ19jb2x1bW5zDQpgYGANCg0KIyMjIFNlcGFyYXRlIGJ5IHNleA0KDQpgYGB7cn0NCmVuZGluZ19pbl9NIDwtIGNoYXJhY3RlcigwKQ0KZW5kaW5nX2luX0YgPC0gY2hhcmFjdGVyKDApDQoNCmZvciAoY29sX25hbWUgaW4gbWF0Y2hpbmdfY29sdW1ucykgew0KICBpZiAoZW5kc1dpdGgoY29sX25hbWUsICJNIikpIHsNCiAgICBlbmRpbmdfaW5fTSA8LSBjKGVuZGluZ19pbl9NLCBjb2xfbmFtZSkNCiAgfSBlbHNlIGlmIChlbmRzV2l0aChjb2xfbmFtZSwgIkYiKSkgew0KICAgIGVuZGluZ19pbl9GIDwtIGMoZW5kaW5nX2luX0YsIGNvbF9uYW1lKQ0KICB9DQp9DQoNCnByaW50KCJDb2x1bW4gbmFtZXMgZW5kaW5nIGluIE06IikNCnByaW50KGVuZGluZ19pbl9NKQ0KDQpwcmludCgiQ29sdW1uIG5hbWVzIGVuZGluZyBpbiBGOiIpDQpwcmludChlbmRpbmdfaW5fRikNCmBgYA0KDQpgYGB7cn0NCmNvaG9ydF9uYW1lc19tIDwtIGMoIlBfMEE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF81QTlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzEwQTE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8xNUExOV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjBBMjRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzI1QTI5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8zMEEzNF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMzVBMzlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzQwQTQ0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF80NUE0OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTBBNTRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzU1QTU5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF82MEE2NF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNjVBNjlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzg1WU1BU19NIikNCg0KY29ob3J0X25hbWVzX2YgPC0gYygiUF8wQTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzVBOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMTBBMTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzE1QTE5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8yMEEyNF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjVBMjlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzMwQTM0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8zNUEzOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNDBBNDRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzQ1QTQ5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF81MEE1NF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTVBNTlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzYwQTY0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF82NUE2OV9GIiwgDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfRiIsIA0KICAgICAgICAgICAgICAgICAgICAiUF84NVlNQVNfRiIpDQoNCg0KDQptdW5pY2lwYWxpdHkgPC0gIkFjYWpldGUiDQpsb2NhbGl0eSA8LSAiU2FuIEphdmllciINCg0KcHVlYl9ub3JtX2ZpbHQgPC0gcHVlYl9ub3JtIHw+IA0KICAgIGZpbHRlcihOT01fTVVOID09IG11bmljaXBhbGl0eSwgTk9NX0xPQyA9PSBsb2NhbGl0eSkNCg0KY29ob3J0X2NvdW50c19tIDwtIGFzLm51bWVyaWMocHVlYl9ub3JtX2ZpbHRbMSxjb2hvcnRfbmFtZXNfbV0pDQpjb2hvcnRfY291bnRzX2YgPC0gYXMubnVtZXJpYyhwdWViX25vcm1fZmlsdFsxLGNvaG9ydF9uYW1lc19mXSkNCg0KZGF0YSA8LSB0aWJibGUoDQogIENvaG9ydCA9IGMoY29ob3J0X25hbWVzX20sIGNvaG9ydF9uYW1lc19mKSwNCiAgQ291bnQgPSBjKGNvaG9ydF9jb3VudHNfbSwgY29ob3J0X2NvdW50c19mKSwNCiAgU2V4ID0gcmVwKGMoIk1hbGUiLCAiRmVtYWxlIiksIGVhY2ggPSBsZW5ndGgoY29ob3J0X25hbWVzX20pKQ0KKQ0KDQojIFBsb3R0aW5nIHBvcHVsYXRpb24gcHlyYW1pZA0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gcmVvcmRlcihDb2hvcnQsIC1Db3VudCksIHkgPSBDb3VudCwgZmlsbCA9IFNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInBpbmsiKSkgKyAgDQogIGNvb3JkX2ZsaXAoKSArICANCiAgbGFicyh0aXRsZSA9ICJQb3B1bGF0aW9uIFB5cmFtaWQiLA0KICAgICAgIHggPSAiUG9wdWxhdGlvbiBDb3VudCIsDQogICAgICAgeSA9ICJBZ2UgQ29ob3J0IiwNCiAgICAgICBmaWxsID0gIlNleCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICANCg0KYGBgDQoNCmBgYHtyfQ0KbmV3X2FnZXMgPC0gYygiMC00IiwNCiAgICAgICAgICAgICAgICAgIjUtOSIsDQogICAgICAgICAgICAgICAgICIxMC0xNCIsDQogICAgICAgICAgICAgICAgICIxNS0xOSIsDQogICAgICAgICAgICAgICAgICIyMC0yNCIsDQogICAgICAgICAgICAgICAgICIyNS0yOSIsIA0KICAgICAgICAgICAgICAgICAiMzAtMzQiLCANCiAgICAgICAgICAgICAgICAgIjM1LTM5IiwgDQogICAgICAgICAgICAgICAgICI0MC00NCIsIA0KICAgICAgICAgICAgICAgICAiNDUtNDkiLA0KICAgICAgICAgICAgICAgICAiNTAtNTQiLA0KICAgICAgICAgICAgICAgICAiNTUtNTkiLA0KICAgICAgICAgICAgICAgICAiNjAtNjQiLCANCiAgICAgICAgICAgICAgICAgIjY1LTY5IiwNCiAgICAgICAgICAgICAgICAgIjcwLTc0IiwgDQogICAgICAgICAgICAgICAgICI3NS03OSIsIA0KICAgICAgICAgICAgICAgICAiODAtODQiLCANCiAgICAgICAgICAgICAgICAgIjg1KyIpDQoNCmRhdGEgPC0gdGliYmxlKA0KICBBZ2UgPSBwYXN0ZTAobmV3X2FnZXMpLA0KICBNYWxlID0gc2FtcGxlKDIwMDoxMDAwLCBsZW5ndGgoY29ob3J0X25hbWVzX20pLCByZXBsYWNlID0gVFJVRSksDQogIEZlbWFsZSA9IHNhbXBsZSgyMDA6MTAwMCwgbGVuZ3RoKGNvaG9ydF9uYW1lc19mKSwgcmVwbGFjZSA9IFRSVUUpDQopDQoNCmRhdGFfbG9uZyA8LSBwaXZvdF9sb25nZXIoDQogIGRhdGEsIA0KICBjb2xzID0gYyhNYWxlLCBGZW1hbGUpLCANCiAgbmFtZXNfdG8gPSAiU2V4IiwgDQogIHZhbHVlc190byA9ICJQb3B1bGF0aW9uIg0KKQ0KDQpiYXNpY19wbG90IDwtIGdncGxvdChkYXRhX2xvbmcsIGFlcyh4ID0gQWdlLCB5ID0gaWZlbHNlKFNleCA9PSAiTWFsZSIsIC1Qb3B1bGF0aW9uLCBQb3B1bGF0aW9uKSwgZmlsbCA9IFNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGFicywgbGltaXRzID0gbWF4KGRhdGFfbG9uZyRQb3B1bGF0aW9uKSAqIGMoLTEsIDEpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIlBvcHVsYXRpb24iLCBmaWxsID0gIlNleCIsIHRpdGxlID0gIlBvcHVsYXRpb24gUHlyYW1pZCIpDQoNCmJhc2ljX3Bsb3QNCmBgYA0KDQojIyMgQ29tcGxldGUgcGlwZWxpbmUNCg0KYGBge3J9DQpjZW5zdXNfZGF0YXNldCA8LSBvcGVuX2RhdGFzZXQoaGVyZSgiZGF0YSIsICJwcm9jZXNzZWQiLCAicGFycXVldF9kYXRhX2Nvb3JkcyIpKQ0KDQoNCnB1ZWJfbm9ybSA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcihOT01fRU5UPT0iUHVlYmxhIikgfD4gDQogICAgY29sbGVjdCgpDQoNCm11bmljaXBhbGl0eSA8LSAiQWNhamV0ZSINCmxvY2FsaXR5IDwtICJTYW4gSmF2aWVyIg0KDQpwdWViX25vcm1fZmlsdCA8LSBwdWViX25vcm0gfD4gDQogICAgZmlsdGVyKE5PTV9NVU4gPT0gbXVuaWNpcGFsaXR5LCBOT01fTE9DID09IGxvY2FsaXR5KQ0KDQoNCmNvaG9ydF9uYW1lc19tIDwtIGMoIlBfMEE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF81QTlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzEwQTE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8xNUExOV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjBBMjRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzI1QTI5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8zMEEzNF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMzVBMzlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzQwQTQ0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF80NUE0OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTBBNTRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzU1QTU5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF82MEE2NF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNjVBNjlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzg1WU1BU19NIikNCg0KY29ob3J0X25hbWVzX2YgPC0gYygiUF8wQTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzVBOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMTBBMTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzE1QTE5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8yMEEyNF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjVBMjlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzMwQTM0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8zNUEzOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNDBBNDRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzQ1QTQ5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF81MEE1NF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTVBNTlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzYwQTY0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF82NUE2OV9GIiwgDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfRiIsIA0KICAgICAgICAgICAgICAgICAgICAiUF84NVlNQVNfRiIpDQoNCg0KDQpuZXdfYWdlcyA8LSBjKCIwLTQiLA0KICAgICAgICAgICAgICAgICAiNS05IiwNCiAgICAgICAgICAgICAgICAgIjEwLTE0IiwNCiAgICAgICAgICAgICAgICAgIjE1LTE5IiwNCiAgICAgICAgICAgICAgICAgIjIwLTI0IiwNCiAgICAgICAgICAgICAgICAgIjI1LTI5IiwgDQogICAgICAgICAgICAgICAgICIzMC0zNCIsIA0KICAgICAgICAgICAgICAgICAiMzUtMzkiLCANCiAgICAgICAgICAgICAgICAgIjQwLTQ0IiwgDQogICAgICAgICAgICAgICAgICI0NS00OSIsDQogICAgICAgICAgICAgICAgICI1MC01NCIsDQogICAgICAgICAgICAgICAgICI1NS01OSIsDQogICAgICAgICAgICAgICAgICI2MC02NCIsIA0KICAgICAgICAgICAgICAgICAiNjUtNjkiLA0KICAgICAgICAgICAgICAgICAiNzAtNzQiLCANCiAgICAgICAgICAgICAgICAgIjc1LTc5IiwgDQogICAgICAgICAgICAgICAgICI4MC04NCIsIA0KICAgICAgICAgICAgICAgICAiODUrIikNCg0KDQpkYXRhIDwtIHRpYmJsZSgNCiAgQWdlID0gcGFzdGUwKG5ld19hZ2VzKSwNCiAgTWFsZSA9IGFzLm51bWVyaWMocHVlYl9ub3JtX2ZpbHRbMSxjb2hvcnRfbmFtZXNfbV0pLA0KICBGZW1hbGUgPSBhcy5udW1lcmljKHB1ZWJfbm9ybV9maWx0WzEsY29ob3J0X25hbWVzX2ZdKQ0KKQ0KDQpkYXRhX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKA0KICBkYXRhLCANCiAgY29scyA9IGMoTWFsZSwgRmVtYWxlKSwgDQogIG5hbWVzX3RvID0gIlNleCIsIA0KICB2YWx1ZXNfdG8gPSAiUG9wdWxhdGlvbiINCikNCmJhc2ljX3Bsb3QgPC0gZ2dwbG90KGRhdGFfbG9uZywgYWVzKHggPSBBZ2UsIHkgPSBpZmVsc2UoU2V4ID09ICJNYWxlIiwgLVBvcHVsYXRpb24sIFBvcHVsYXRpb24pLCBmaWxsID0gU2V4KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gYWJzLCBsaW1pdHMgPSBtYXgoZGF0YV9sb25nJFBvcHVsYXRpb24pICogYygtMSwgMSkpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiUG9wdWxhdGlvbiIsIGZpbGwgPSAiU2V4IiwgdGl0bGUgPSAiUG9wdWxhdGlvbiBQeXJhbWlkIikNCg0KYmFzaWNfcGxvdA0KYGBgDQoNCiMjIyBDYXJkDQoNCmBgYHtyfQ0KY2FyZCA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcigNCiAgICAgIE5PTV9FTlQgPT0gIlB1ZWJsYSIsDQogICAgICBOT01fTVVOID09ICJBY2F0ZW5vIiwNCiAgICAgIE5PTV9MT0MgPT0gIlNhbnRhIEFuZHJlYSINCiAgICApIHw+DQogIGNvbGxlY3QoKQ0KICAgIA0KICAgIHRvdGFsX3BvcHVsYXRpb24gPC0gYXMubnVtZXJpYyhjYXJkWzEsIGMoIlBPQlRPVCIpXSwgbmEucm0gPSBUUlVFKQ0KICAgIHBhc3RlKCJUb3RhbCBQb3B1bGF0aW9uOiIsIHRvdGFsX3BvcHVsYXRpb24pDQpgYGANCg0KIyMjIFBpZQ0KDQpgYGB7cn0NCnRvdCA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcihOT01fRU5UPT0iVG90YWwgbmFjaW9uYWwiKSB8PiANCiAgICBjb2xsZWN0KCkNCg0KdG90DQpgYGANCg0KYGBge3J9DQojIG9yaWdpbiA8LSBjZW5zdXNfZGF0YXNldCB8PiAgDQojICAgICAgZmlsdGVyKA0KIyAgICAgICBOT01fRU5UID09ICJQdWVibGEiLA0KIyAgICAgICBOT01fTVVOID09ICJBY2F0ZW5vIiwNCiMgICAgICAgTk9NX0xPQyA9PSAiU2FudGEgQW5kcmVhIg0KIyAgICAgKSB8Pg0KIyAgIGNvbGxlY3QoKQ0KICAgIA0KdG90DQogICAgDQogICAgIyBFeHRyYWN0IGJpcnRoIGRhdGENCiAgICBiaXJ0aF9sb2NhbCA8LSBhcy5udW1lcmljKHRvdFsxLCAiUE5BQ0VOVCJdKQ0KICAgIGJpcnRoX2Fub3RoZXIgPC0gYXMubnVtZXJpYyh0b3RbMSwgIlBOQUNPRSJdKQ0KICAgIA0KICAgICMgRGVidWdnaW5nIG91dHB1dA0KICAgIHByaW50KHBhc3RlKCJCaXJ0aCBMb2NhbDoiLCBiaXJ0aF9sb2NhbCkpDQogICAgcHJpbnQocGFzdGUoIkJpcnRoIEFub3RoZXI6IiwgYmlydGhfYW5vdGhlcikpDQogICAgDQogICAgIyBDcmVhdGUgcmF0aW8gZGF0YWZyYW1lDQogICAgcmF0aW9fZGYgPC0gdGliYmxlKA0KICAgICAgQ2F0ZWdvcnkgPSBjKCJMb2NhbCIsICJPdGhlciIpLA0KICAgICAgUmF0aW8gPSBjKGJpcnRoX2xvY2FsLCBiaXJ0aF9hbm90aGVyKQ0KICAgICkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBwZXJjZW50YWdlcw0KICAgIHJhdGlvX2RmJFBlcmNlbnRhZ2UgPC0gcmF0aW9fZGYkUmF0aW8gLyBzdW0ocmF0aW9fZGYkUmF0aW8pICogMTAwDQogICAgDQogICAgIyBQbG90IHRoZSBwaWUgY2hhcnQNCiAgICBnZyA8LSBnZ3Bsb3QocmF0aW9fZGYsIGFlcyh4ID0gIiIsIHkgPSBSYXRpbywgZmlsbCA9IENhdGVnb3J5KSkgKw0KICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICAgICAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsNCiAgICAgIHRoZW1lX3ZvaWQoKSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQkZDNCIsICIjRjg3NjZEIikpICsNCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQoUGVyY2VudGFnZSksICIlIikpLCANCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikNCiAgICANCiAgICBnZw0KICAgIA0KYGBgDQoNCiMjIyBSZWFkIENTViBmcm9tIFVSTA0KDQpgYGB7cn0NCnRlc3RfY3N2IDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWIudWJjLmNhL01EUy0yMDIzLTI0L0RTQ0lfNTMyX2luZGl2aWR1YWwtYXNzaWdubWVudF9tYXJjb255MS9tYXN0ZXIvZGF0YS9wcm9jZXNzZWQvZGF0YV9jb29yZHMuY3N2P3Rva2VuPUdIU0FUMEFBQUFBQUFBQUNMTzVXSzM2V0o2Uzc0RTNOVTRDWlJKSkpHQSIpDQoNCnRlc3RfY3N2DQoNCmBgYA0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGEgPC0gZmlsdGVyKHRlc3RfY3N2LCBOT01fRU5UID09ICJQdWVibGEiKQ0KZmlsdGVyZWRfZGF0YQ0KYGBgDQoNCiMjIyBEb3dubG9hZCB3aXRoIGN1cmwNCg0KYGBge3J9DQpsaWJyYXJ5KGN1cmwpDQoNCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvbWFzdGVyL2RhdGEvcHJvY2Vzc2VkL2RhdGFfY29vcmRzLmNzdj90b2tlbj1HSFNBVDBBQUFBQUFBQUFDTE81V0szNldKNlM3NEUzTlU0Q1pSSkpKR0EiDQoNCnJlc3BvbnNlIDwtIGN1cmw6OmN1cmxfZmV0Y2hfbWVtb3J5KHVybCkNCg0KaWYgKHJlc3BvbnNlJHN0YXR1c19jb2RlID09IDIwMCkgew0KICBjc3ZfY29udGVudCA8LSByYXdUb0NoYXIocmVzcG9uc2UkY29udGVudCkNCiAgICB0ZXN0X2NzdiA8LSByZWFkLmNzdih0ZXh0ID0gY3N2X2NvbnRlbnQpDQpwcmludCh0ZXN0X2NzdikNCn0NCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoUkN1cmwpDQp4IDwtIGdldFVSTCgiaHR0cHM6Ly9yYXcuZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvbWFzdGVyL2RhdGEvcHJvY2Vzc2VkL2RhdGFfY29vcmRzLmNzdj90b2tlbj1HSFNBVDBBQUFBQUFBQUFDTE81V0szNldKNlM3NEUzTlU0Q1pSSkpKR0EiKQ0KeSA8LSByZWFkLmNzdih0ZXh0ID0geCkNCnkNCmBgYA0KDQpgYGB7cn0NCiMgdGVzdF8yIDwtIGFycm93OjpyZWFkX3BhcnF1ZXQoImh0dHBzOi8vZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvdHJlZS9tYXN0ZXIvZGF0YS9wcm9jZXNzZWQvcGFycXVldF9kYXRhX2Nvb3Jkcy5wYXJxdWV0IikNCmBgYA0K